﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using NetFrameworkExtensions;

namespace SoftwareEngineering.Modeling
{
  public class ModelValidationResult : ValidationResult
  {
    public ModelBase ModelRoot { get; private set; }
    public ModelBase Model { get; private set; }
    public IEnumerable<Tup<PropertyInfo, ModelBase>> PathToModel { get; private set; }

    public ModelValidationResult(string errorMessage, IEnumerable<string> memberNames, ValidationContext context)
      : base(errorMessage, memberNames)
    {
      InitFromContext(context);
    }

    public ModelValidationResult(string errorMessage, ValidationContext context)
      : base(errorMessage)
    {
      InitFromContext(context);
    }

    public ModelValidationResult(string errorMessage, ModelBase model, ModelBase modelRoot, IEnumerable<Tup<PropertyInfo, ModelBase>> pathToModel)
      : base(errorMessage)
    {
      ModelRoot = modelRoot;
      Model = model;
      PathToModel = pathToModel;
    }

    public ModelValidationResult(string errorMessage, IEnumerable<string> memberNames, ModelBase model, ModelBase modelRoot, IEnumerable<Tup<PropertyInfo, ModelBase>> pathToModel)
      : base(errorMessage, memberNames)
    {
      ModelRoot = modelRoot;
      Model = model;
      PathToModel = pathToModel;
    }

    public ModelValidationResult(ModelValidationResult other)
      : base(other)
    {
      ModelRoot = other.ModelRoot;
      Model = other.Model;
      PathToModel = other.PathToModel;
    }

    public ModelValidationResult(ValidationResult other, ValidationContext context)
      : base(other)
    {
      if (context != null)
        InitFromContext(context);
      else if (other is ModelValidationResult)
      {
        var mvr = other as ModelValidationResult;
        ModelRoot = mvr.ModelRoot;
        Model = mvr.Model;
        PathToModel = mvr.PathToModel;
      }
    }

    private void InitFromContext(ValidationContext context)
    {
      if (context == null) return;
      Model = context.ObjectInstance as ModelBase;
      if (context.Items != null)
      {
        object modelRoot, pathToModel;
        if (context.Items.TryGetValue(ModelValidator.ContextRoot, out modelRoot))
          ModelRoot = modelRoot as ModelBase;
        if (context.Items.TryGetValue(ModelValidator.ContextPath, out pathToModel))
          PathToModel = (pathToModel as IEnumerable<Tup<PropertyInfo, ModelBase>>).GetAsEmptyIfNull().ToArray();
      }
    }

    const string PathSeparator = " => ";
    public override string ToString()
    {
      var sb = new StringBuilder();
      sb.Append(ErrorMessage);
      sb.Append(" on members: ")
        .Append(MemberNames.ToStringList(", ", m => "\"" + m + "\"", closing: ".", empty: "none."));
      if (Model != null)
        if (ModelRoot != null && PathToModel != null && PathToModel.Any())
          sb.Append(" ")
            .Append(ModelRoot.ToString())
            .Append(PathSeparator)
            .Append(PathToModel.ToStringList(PathSeparator, p => p.Item1.Name + PathSeparator + p.Item2.ToString()));
        else
          sb.Append(" ")
            .Append(Model.ToString());

      return sb.ToString();
    }
  }
}
